#pragma once

#include <vector>

#ifdef PUNCH_MSGPACK
#include <msgpack.hpp>
#endif

namespace mdtx::punch::tradesim
{
    /**
     * @brief Records client positions
     *
     * @tparam QT quote type
     */
    template <typename QT>
    class Position
    {
    public:
        using id_type = typename QT::id_type;
        using quant_type = typename QT::quant_type;

        struct PositionT
        {
            quant_type net0, net, avail, tnew, borrow0, borrow,
                tnvr_buy, quant_buy, tnvr_sell, quant_sell;

#ifdef PUNCH_MSGPACK
            MSGPACK_DEFINE_ARRAY(net0, net, avail, tnew, borrow0, borrow,
                                 tnvr_buy, quant_buy, tnvr_sell, quant_sell);
#endif
        };

        static auto create(quant_type share)
        {
            if (share < 0)
            {
                return PositionT{share, share, quant_type{}, quant_type{}, -share, -share,
                                 quant_type{}, quant_type{}, quant_type{}, quant_type{}};
            }
            else
            {
                return PositionT{share, share, share, quant_type{}, quant_type{}, quant_type{},
                                 quant_type{}, quant_type{}, quant_type{}, quant_type{}};
            }
        }

    private:
        size_t n_ins, n_cli;
        std::vector<PositionT> val;

    public:
        Position(size_t n_ins, size_t n_cli)
            : n_ins(n_ins), n_cli(n_cli), val(n_ins * n_cli, create(quant_type{}))
        {
        }

        size_t num_instr() const
        {
            return n_ins;
        }

        size_t num_client() const
        {
            return n_cli;
        }

        auto &get(id_type cli_id, id_type ins_id)
        {
            size_t idx = n_ins * cli_id + ins_id;
            return val[idx];
        }

        const auto &get(id_type cli_id, id_type ins_id) const
        {
            size_t idx = n_ins * cli_id + ins_id;
            return val[idx];
        }

        template <class ItQuant>
        void set(ItQuant all_holdings)
        {
            for (size_t idx = 0; idx < n_ins * n_cli; ++idx)
            {
                val[idx] = create(*all_holdings++);
            }
        }

        template <class ItQuant>
        void set(id_type cli_id, ItQuant all_holdings)
        {
            size_t idx = n_ins * cli_id;
            for (size_t offset = 0; offset < n_ins; ++offset)
            {
                val[idx + offset] = create(*all_holdings++);
            }
        }

        id_type new_client()
        {
            for (size_t offset = 0; offset < n_ins; ++offset)
            {
                val.push_back(create(quant_type{}));
            }
            return n_cli++;
        }

        template <class ItQuant>
        id_type new_client(ItQuant all_holdings)
        {
            for (size_t offset = 0; offset < n_ins; ++offset)
            {
                val.push_back(create(*all_holdings++));
            }
            return n_cli++;
        }
    };
}
